/******************************************************************************
 * program:     rasimg library                                                *
 * function:    Module for JBIG support                                       *
 * modul:       ras_jbg.cc                                                    *
 * licency:     GPL or LGPL                                                   *
 ******************************************************************************/
#include <stdio.h>
#include <stdlib.h>

#include "imgsupp.h"
#include "image.h"

// https://www.cl.cam.ac.uk/~mgk25/jbigkit/


#if defined(SupportJBIG) && SupportJBIG>0

#include <jbig.h>

#ifdef __MSDOS__
 #define MaxBufferSize	4096
#else
 #define MaxBufferSize	8192
#endif


#if SupportJBIG>=4 || SupportJBIG==2
Image LoadPictureJBIG(const char *Name)
{
Image Img;
FILE *F = NULL;
Raster2DAbstract *Raster=NULL;
struct jbg_dec_state sjd;
size_t bytes_read = 0;
uint8_t *buffer;
uint8_t *p;
int status;
char RealPlanes = 0;

  buffer = (unsigned char *) malloc(MaxBufferSize);
  if(!buffer) return(NULL);

  if((F=fopen(Name,"rb"))==NULL)
  {
    free(buffer);
    return(NULL);
  }

  jbg_dec_init(&sjd);
  do
  {
    size_t length = fread((char*)buffer,1,MaxBufferSize,F);
    if(length == 0) break;
    p = buffer;
    size_t count = 0;
    while(length > 0)
    {
      status = jbg_dec_in(&sjd,p,length,&count);
      p += count;
      length -= count;
      if(status!=JBG_EAGAIN) break;
    }
  } while(status == JBG_EAGAIN);
  if(JBG_EOK != status) goto ENDPROC;

  const int planes = jbg_dec_getplanes(&sjd);
  switch(planes)
  {
    default:
    case 0: goto ENDPROC;
    case 1: RealPlanes=1; break;
    case 2: RealPlanes=2; break;
    case 3:
    case 4: RealPlanes=4; break;
    case 5:
    case 6:
    case 7:
    case 8: RealPlanes=8; break;
    case 9:
    case 10:
    case 11:
    case 12:
    case 13:
    case 14:
    case 15:
    case 16: RealPlanes=16; break;
  }

  Raster = CreateRaster2D(jbg_dec_getwidth(&sjd), jbg_dec_getheight(&sjd), RealPlanes);
  if(Raster==NULL) goto ENDPROC;
  if(Raster->Data2D==NULL || Raster->Size1D==0 || Raster->Size2D==0)
  {
    delete Raster;
    Raster = NULL;
    goto ENDPROC;
  }

  status = (Raster->Size1D+7) >> 3;	// row width
  if(RealPlanes==1)
  {    
    p = jbg_dec_getimage(&sjd,0);  
    for(unsigned y=0; y<Raster->Size2D; y++)
    {
      memcpy(Raster->GetRow(y), p, status);
      p += status;
    }
  }
  else
  {
    for(unsigned CurPlane=0; CurPlane<planes; CurPlane++)	// 0 is most signifficant bit.
    {
      RealPlanes--;
      p = jbg_dec_getimage(&sjd,CurPlane);
      for(unsigned y=0; y<Raster->Size2D; y++)
      {
        Raster->GetRowRaster(y)->Join1Bit(p,RealPlanes);
        p += status;
      }
    }
  }

ENDPROC:
  jbg_dec_free(&sjd);
  free(buffer);
  fclose(F);
  /* At this point you may want to check to see whether any corrupt-data
   * warnings occurred (test whether jerr.pub.num_warnings is nonzero). */

  Img.AttachRaster(Raster);
  return(Img);  /* And we're done! */
}
#endif


#if SupportJBIG>=3

static void output_bie(unsigned char *start, size_t len, void *file)
{
  fwrite(start, 1, len, (FILE*)file);  
  return;
}


int SavePictureJBIG(const char *Name, const Image &Img)
{
FILE *F;
uint8_t **bitmaps = NULL;
struct jbg_enc_state se;
const double jbig_lib_version = strtod(JBG_VERSION, (char **)NULL);
unsigned i;
int result = 0;

  if(Img.Raster==NULL) return ErrEmptyRaster;
  const int Planes = Img.Raster->GetPlanes();
  if(Planes>16 || Planes<=0) return ErrUnsupportedResolution;
  const unsigned bytes_per_row = (Img.Raster->Size1D+7) >> 3;  

  bitmaps = (uint8_t**)calloc(Img.Raster->GetPlanes(),sizeof(uint8_t*));  // calloc does 0 fill
  if(bitmaps==NULL) return ErrNoMem;
  for(i=0; i<Planes; i++)
  {
    bitmaps[i] = (uint8_t*)malloc(bytes_per_row*Img.Raster->Size2D);
    if(bitmaps[i]==NULL) {result=ErrNoMem; goto Finish;}
  }

  if((F=fopen(Name,"wb"))==NULL) {result=ErrOpenFile; goto Finish;}

  if(Planes==1)
  {
    for(unsigned y=0; y<Img.Raster->Size2D; y++)
        memcpy(bitmaps[0]+y*bytes_per_row, Img.Raster->GetRow(y), bytes_per_row);
  }
  else
  {    
    for(unsigned y=0; y<Img.Raster->Size2D; y++)
    {
      Raster1DAbstract *RR1y = Img.Raster->GetRowRaster(y);
      if(RR1y != NULL)
        for(i=0; i<Planes; i++)
          RR1y->Peel1Bit(bitmaps[i]+y*bytes_per_row,Planes-i-1);
    }
  }

  jbg_enc_init(&se, Img.Raster->Size1D, Img.Raster->Size2D, Planes, bitmaps, output_bie, F); /* initialize encoder */

  jbg_enc_lrange(&se,-1,-1);
  jbg_enc_options(&se,					// Encoder state
                  JBG_ILEAVE| JBG_SMID,			// Order
                  JBG_TPDON | JBG_TPBON | JBG_DPON,	// Options                    
                  (jbig_lib_version < 1.6 ? -1 : 0),	// Lines per stripe in resolution layer 0. (was -1)
                  -1,			/* mx */
                  -1);			/* my */

  jbg_enc_out(&se);                                    /* encode image */

  jbg_enc_free(&se);                    /* release allocated resources */

Finish:  
  if(F) {fclose(F); F=NULL;}
  if(bitmaps)
  {
    for(i=0; i<Img.Raster->GetPlanes(); i++)
    {
      if(bitmaps[i]==NULL) continue;
      free(bitmaps[i]);
      bitmaps[i] = NULL;
    }
    free(bitmaps);
    bitmaps = NULL;
  }
  return 0;
}
#endif

#endif
